package cn.wlinker.video.monitor.stream;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.UUID;

/**
 * flv服务实现
 *
 * @author gxsjx
 * @version 1.0
 * @date 2025/5/3
 * Copyright © wlinker.cn
 */
@Slf4j
public class FlvServiceImpl implements IFlvService {


    private FlvServiceImpl() {
    }

    public static final FlvServiceImpl INSTANCE = new FlvServiceImpl();

    /**
     * 打开一个流地址，写入response
     *
     * @param streamId 流ID
     * @param response 响应请求
     * @author xufeng
     */
    @Override
    public void open(InputStream inputStream, String streamId, HttpServletResponse response) {
        //创建转换器线程并启动
        Converter c = ConverterRegistration.isExistStream(streamId);
        if(c == null || !c.isRunning()){
            c = ConverterRegistration.open(inputStream, streamId);
        }
        //UUID设置一个key值
        String key = UUID.randomUUID().toString();
        //创建输出字节流
        OutputStreamEntity outEntity = new OutputStreamEntity(new ByteArrayOutputStream(), System.currentTimeMillis(), key);
        //添加流输出
        log.info("==添加流输出=={}", key);
        c.addOutputStreamEntity(key, outEntity);
        try {
            //设置响应头
            response.setContentType("video/x-flv");
            response.setHeader("Connection", "keep-alive");
            response.setStatus(HttpServletResponse.SC_OK);
            //写出缓冲信息，并清空
            response.flushBuffer();
            //循环读取outEntity里的流输出给前台
            log.info("{}==(response)循环读取outEntity里的流输出给前台==", c.getConverterState());
            readFlvStream(c, outEntity, response);
        } catch (Exception e) {
            //客户端长连接过程中被异常关闭，关闭该长连接对应的转换器线程
            //客户端长连接过程中被异常关闭，关闭该长连接对应的转换器线程
            c.removeOutputStreamEntity(outEntity.getKey());
            if (c.allOutEntity().isEmpty()) {
                c.exit();
                log.info("关闭转换流：{}", c.getUrl());
            }
            log.error(StrUtil.format("streamId:{}", streamId), e);
        }
    }

    /**
     * 打开一个流地址，写入response
     *
     * @param url      流地址
     * @param streamId 流ID
     * @param response 响应请求
     * @author xufeng
     */
    @Override
    public void open(String url, String streamId, HttpServletResponse response) {
        //创建转换器线程并启动
        Converter c = ConverterRegistration.open(url, streamId);
        //UUID设置一个key值
        String key = UUID.randomUUID().toString();
        //创建输出字节流
        OutputStreamEntity outEntity = new OutputStreamEntity(new ByteArrayOutputStream(), System.currentTimeMillis(), key);
        //添加流输出
        log.info("==添加流输出=={}", key);
        c.addOutputStreamEntity(key, outEntity);
        try {
            //设置响应头
            response.setContentType("video/x-flv");
            response.setHeader("Connection", "keep-alive");
            response.setStatus(HttpServletResponse.SC_OK);
            //写出缓冲信息，并清空
            response.flushBuffer();
            //循环读取outEntity里的流输出给前台
            log.info("{}==(response)循环读取outEntity里的流输出给前台==", c.getConverterState());
            readFlvStream(c, outEntity, response);
        } catch (Exception e) {
            //客户端长连接过程中被异常关闭，关闭该长连接对应的转换器线程
            c.removeOutputStreamEntity(outEntity.getKey());
            if (c.allOutEntity().isEmpty()) {
                c.exit();
                log.info("关闭转换流：{}", c.getUrl());
            }
            log.error(StrUtil.format("streamId:{}", streamId), e);
        }
    }

    /**
     * 递归读取转换好的视频流
     *
     * @param c         转换器
     * @param outEntity 输出流
     * @param response  响应
     * @throws Exception
     * @author xufeng
     */
    public void readFlvStream(Converter c, OutputStreamEntity outEntity, HttpServletResponse response)
            throws Exception {
           while(true){
               //根据转换器状态来决定是继续等待、读取、结束流输出
               switch (c.getConverterState()) {
                   case INITIAL:
                       Thread.sleep(300);
                       continue;
                   case OPEN:
                       Thread.sleep(100);
                       //System.out.println("=== OPEN递归读取转换好的视频流=="+c.getUrl());
                       continue;
                   case RUN:
                       if (outEntity.getOutput().size() > 0) {
                           byte[] b = outEntity.getOutput().toByteArray();
                           outEntity.getOutput().reset();
                           try {
                               ServletOutputStream outputStream = response.getOutputStream();
                               outputStream.write(b);
                           } catch (ClientAbortException e) {
                               log.info("客户端中断连接: {}", e.getMessage());
                               c.removeOutputStreamEntity(outEntity.getKey());
                               if (c.allOutEntity().isEmpty()) {
                                   c.exit();
                                   log.info("关闭转换流：{}", c.getUrl());
                               }
                               return;
                           }
                           outEntity.setUpdateTime(System.currentTimeMillis());
                       }
                       //System.out.println("=== RUN递归读取转换好的视频流=="+c.getUrl());
                       c.setUpdateTime(System.currentTimeMillis());
                       Thread.sleep(100);
                       continue;
                   case CLOSE:
                       log.info("close");
                       return;
                   default:
                       return;
               }
           }
    }
}